﻿using System;
using System.Collections;
using System.Web.UI;

namespace Trentacular.SharePoint.Web.UI.WebControls
{
    /// <summary>
    /// Data source control that delegates all operations as events to be handled in
    /// the code-behind of the page or user control where it is used.
    /// </summary>
    public class DelegateDataSource : DataSourceControl
    {
        /// <summary>
        /// Gets or sets a value indicating whether this instance can page.
        /// </summary>
        /// <value><c>true</c> if this instance can page; otherwise, <c>false</c>.
        /// </value>
        public bool CanPage { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether this instance can retrieve total
        /// row count.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance can retrieve total row count; otherwise,
        /// 	<c>false</c>.
        /// </value>
        public bool CanRetrieveTotalRowCount { get; set; }

        private bool _canSort;

        /// <summary>
        /// Gets or sets a value indicating whether this instance can sort.
        /// </summary>
        /// <value><c>true</c> if this instance can sort; otherwise, <c>false</c>.
        /// </value>
        public bool CanSort
        {
            get
            {
                if (AutoSort) return true;
                return _canSort;
            }
            set { _canSort = value; }
        }

        /// <summary>
        /// If true, the results of the ExecuteSelect operation will
        /// automatically be sorted based on the SortExpression, assuming
        /// the SortExpression corresponds to a Property name.  Setting
        /// AutoSort to true implies CanSort is true.
        /// </summary>
        public bool AutoSort { get; set; }

        /// <summary>
        /// Occurs when the data source needs to reteive data.
        /// </summary>
        public event Action<DelegateSelectArguments> Select;

        /// <summary>
        /// Occurs when the data source needs to insert data.
        /// </summary>
        public event Action<DelegateInsertArguments> Insert;

        /// <summary>
        /// Occurs when the datasource needs to update data.
        /// </summary>
        public event Action<DelegateUpdateArguments> Update;

        /// <summary>
        /// Occurs when the data source needs to delete data.
        /// </summary>
        public event Action<DelegateDeleteArguments> Delete;

        private DelegateDataSourceView _view;

        /// <summary>
        /// Gets the data source view.
        /// </summary>
        /// <value>The data source view.</value>
        public DelegateDataSourceView View
        {
            get
            {
                if (_view == null)
                    _view = new DelegateDataSourceView(this);
                return _view;
            }
        }

        /// <summary>
        /// Gets the named data source view associated with the data source control.
        /// </summary>
        /// <param name="viewName">The name of the
        /// <see cref="T:System.Web.UI.DataSourceView"/> to retrieve. In data source
        /// controls that support only one view, such as
        /// <see cref="T:System.Web.UI.WebControls.SqlDataSource"/>, this parameter is
        /// ignored.</param>
        /// <returns>
        /// Returns the named <see cref="T:System.Web.UI.DataSourceView"/> associated
        /// with the <see cref="T:System.Web.UI.DataSourceControl"/>.
        /// </returns>
        protected override DataSourceView GetView(string viewName)
        {
            return View;
        }

        /// <summary>
        /// Gets a list of data from the underlying data storage.
        /// </summary>
        /// <param name="arguments">A
        /// <see cref="T:System.Web.UI.DataSourceSelectArguments"/> that is used to
        /// request operations on the data beyond basic data retrieval.</param>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerable"/> list of data from the
        /// underlying data storage.
        /// </returns>
        public virtual IEnumerable OnSelect(DataSourceSelectArguments arguments)
        {
            if (Select == null)
                throw new InvalidOperationException(
                    string.Format(
                        "Missing Select delegate on DelegateDataSource control '{0}'.  Please specify the delegate method to perform the select operation.",
                        ID
                        )
                    );

            var selectArgs = new DelegateSelectArguments(arguments);
            Select(selectArgs);
            return selectArgs.Results;
        }

        /// <summary>
        /// Performs an insert operation on the list of data that the
        /// <see cref="T:System.Web.UI.DataSourceView"/> object represents.
        /// </summary>
        /// <param name="values">An <see cref="T:System.Collections.IDictionary"/> of
        /// name/value pairs used during an insert operation.</param>
        /// <returns>
        /// The number of items that were inserted into the underlying data storage.
        /// </returns>
        public virtual int OnInsert(IDictionary values)
        {
            if (Insert == null)
                throw new InvalidOperationException(
                    string.Format(
                        "Missing Insert delegate on DelegateDataSource control '{0}'.  Please specify the delegate method to perform the insert operation.",
                        ID
                        )
                    );

            var insertArgs = new DelegateInsertArguments(values);
            Insert(insertArgs);
            return insertArgs.ResultCount;
        }

        /// <summary>
        /// Performs an update operation on the list of data that the
        /// <see cref="T:System.Web.UI.DataSourceView"/> object represents.
        /// </summary>
        /// <param name="keys">An <see cref="T:System.Collections.IDictionary"/> of
        /// object or row keys to be updated by the update operation.</param>
        /// <param name="values">An <see cref="T:System.Collections.IDictionary"/> of
        /// name/value pairs that represent data elements and their new values.
        /// </param>
        /// <param name="oldValues">An <see cref="T:System.Collections.IDictionary"/>
        /// of name/value pairs that represent data elements and their original values.
        /// </param>
        /// <returns>
        /// The number of items that were updated in the underlying data storage.
        /// </returns>
        public virtual int OnUpdate(
            IDictionary keys,
            IDictionary values,
            IDictionary oldValues
            )
        {
            if (Update == null)
                throw new InvalidOperationException(
                    string.Format(
                        "Missing Update delegate on DelegateDataSource control '{0}'.  Please specify the delegate method to perform the update operation.",
                        ID
                        )
                    );

            var updateArgs = new DelegateUpdateArguments(keys, values, oldValues);
            Update(updateArgs);
            return updateArgs.ResultCount;
        }

        /// <summary>
        /// Performs a delete operation on the list of data that the
        /// <see cref="T:System.Web.UI.DataSourceView"/> object represents.
        /// </summary>
        /// <param name="keys">An <see cref="T:System.Collections.IDictionary"/> of
        /// object or row keys to be deleted by the
        /// <see cref="M:System.Web.UI.DataSourceView.ExecuteDelete(System.Collections.IDictionary,System.Collections.IDictionary)"/>
        /// operation.</param>
        /// <param name="oldValues">An <see cref="T:System.Collections.IDictionary"/>
        /// of name/value pairs that represent data elements and their original values.
        /// </param>
        /// <returns>
        /// The number of items that were deleted from the underlying data storage.
        /// </returns>
        public virtual int OnDelete(IDictionary keys, IDictionary oldValues)
        {
            if (Delete == null)
                throw new InvalidOperationException(
                    string.Format(
                        "Missing Delete delegate on DelegateDataSource control '{0}'.  Please specify the delegate method to perform the delete operation.",
                        ID
                        )
                    );

            var deleteArgs = new DelegateDeleteArguments(keys, oldValues);
            Delete(deleteArgs);
            return deleteArgs.ResultCount;
        }
    }

    public class DelegateSelectArguments
    {
        public DelegateSelectArguments(DataSourceSelectArguments arguments)
        {
            Arguments = arguments;
        }

        public DataSourceSelectArguments Arguments { get; private set; }
        public IEnumerable Results { get; set; }
    }

    public class DelegateInsertArguments
    {
        public DelegateInsertArguments(IDictionary values)
        {
            Values = values;
        }

        public IDictionary Values { get; private set; }
        public int ResultCount { get; set; }
    }

    public class DelegateUpdateArguments
    {
        public DelegateUpdateArguments(
            IDictionary keys,
            IDictionary values,
            IDictionary oldValues
            )
        {
            Keys = keys;
            Values = values;
            OldValues = oldValues;
        }

        public IDictionary Keys { get; private set; }
        public IDictionary Values { get; private set; }
        public IDictionary OldValues { get; private set; }
        public int ResultCount { get; set; }
    }

    public class DelegateDeleteArguments
    {
        public DelegateDeleteArguments(IDictionary keys, IDictionary oldValues)
        {
            Keys = keys;
            OldValues = oldValues;
        }

        public IDictionary Keys { get; private set; }
        public IDictionary OldValues { get; private set; }
        public int ResultCount { get; set; }
    }
}
